#include "InputManager.h"

#include <CCStdC.h>
#include <CCEGLView.h>
#include <sstream>

InputManager *InputManager::m_inputManager;
 
InputManager::InputManager( void ) :
    m_mouse( 0 ),
    m_keyboard( 0 ),
    m_inputSystem( 0 ) {
}
 
InputManager::~InputManager( void ) {
    if( m_inputSystem ) {
        if( m_mouse ) {
            m_inputSystem->destroyInputObject( m_mouse );
            m_mouse = 0;
        }
 
        if( m_keyboard ) {
            m_inputSystem->destroyInputObject( m_keyboard );
            m_keyboard = 0;
        }
 
        if( m_joysticks.size() > 0 ) {
            m_itJoystick    = m_joysticks.begin();
            m_itJoystickEnd = m_joysticks.end();
            for(; m_itJoystick != m_itJoystickEnd; ++m_itJoystick ) {
                m_inputSystem->destroyInputObject( *m_itJoystick );
            }
 
            m_joysticks.clear();
        }
 
        // If you use OIS1.0RC1 or above, uncomment this line
        // and comment the line below it
        m_inputSystem->destroyInputSystem( m_inputSystem );
        //mInputSystem->destroyInputSystem();
        m_inputSystem = 0;
 
        // Clear Listeners
        m_keyListeners.clear();
        m_mouseListeners.clear();
        m_joystickListeners.clear();
    }
}
 
void InputManager::initialise( ) 
{
    if( !m_inputSystem )
	{
        // Setup basic variables
        OIS::ParamList paramList;    
        size_t hWnd = 0;
        //std::ostringstream windowHndStr;
		std::stringstream ss;
 
        // Get window handle
        //renderWindow->getCustomAttribute( "WINDOW", &hWnd );
		cocos2d::CCEGLView& eglView = cocos2d::CCEGLView::sharedOpenGLView();
		hWnd = (unsigned int) eglView.getHWnd();

        // Fill parameter list
        //windowHndStr << (unsigned int) windowHnd;
		ss << hWnd;
		paramList.insert(OIS::ParamList::value_type("WINDOW", ss.str()));

		paramList.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND" )));
		paramList.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));
		paramList.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_FOREGROUND")));
		paramList.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_NONEXCLUSIVE")));
 
        // Create inputsystem
        m_inputSystem = OIS::InputManager::createInputSystem( paramList );
 
        // If possible create a buffered keyboard
        // (note: if below line doesn't compile, try:  if (mInputSystem->getNumberOfDevices(OIS::OISKeyboard) > 0) {
        //if( mInputSystem->numKeyboards() > 0 ) {
        if (m_inputSystem->getNumberOfDevices(OIS::OISKeyboard) > 0) {
            m_keyboard = static_cast<OIS::Keyboard*>( m_inputSystem->createInputObject( OIS::OISKeyboard, true ) );
            m_keyboard->setEventCallback( this );
        }
 
        // If possible create a buffered mouse
        // (note: if below line doesn't compile, try:  if (mInputSystem->getNumberOfDevices(OIS::OISMouse) > 0) {
        //if( mInputSystem->numMice() > 0 ) {
        if (m_inputSystem->getNumberOfDevices(OIS::OISMouse) > 0) {
            m_mouse = static_cast<OIS::Mouse*>( m_inputSystem->createInputObject( OIS::OISMouse, true ) );
            m_mouse->setEventCallback( this );
 
            // Get window size
            unsigned int width, height, depth;
            int left, top;
			width = eglView.getFrameSize().width;
			height = eglView.getFrameSize().height;
 
            // Set mouse region
            this->setWindowExtents( width, height );
        }
 
        // If possible create all joysticks in buffered mode
        // (note: if below line doesn't compile, try:  if (mInputSystem->getNumberOfDevices(OIS::OISJoyStick) > 0) {
        //if( mInputSystem->numJoySticks() > 0 ) {
        if (m_inputSystem->getNumberOfDevices(OIS::OISJoyStick) > 0) {
            //mJoysticks.resize( mInputSystem->numJoySticks() );
            m_joysticks.resize( m_inputSystem->getNumberOfDevices(OIS::OISJoyStick) );
 
            m_itJoystick    = m_joysticks.begin();
            m_itJoystickEnd = m_joysticks.end();
            for(; m_itJoystick != m_itJoystickEnd; ++m_itJoystick ) {
                (*m_itJoystick) = static_cast<OIS::JoyStick*>( m_inputSystem->createInputObject( OIS::OISJoyStick, true ) );
                (*m_itJoystick)->setEventCallback( this );
            }
        }
    }
}
 
void InputManager::capture( void ) {
    // Need to capture / update each device every frame
    if( m_mouse ) {
        m_mouse->capture();
    }
 
    if( m_keyboard ) {
        m_keyboard->capture();
    }
 
    if( m_joysticks.size() > 0 ) {
        m_itJoystick    = m_joysticks.begin();
        m_itJoystickEnd = m_joysticks.end();
        for(; m_itJoystick != m_itJoystickEnd; ++m_itJoystick ) {
            (*m_itJoystick)->capture();
        }
    }
}
 
void InputManager::addKeyListener( OIS::KeyListener *keyListener, const std::string& instanceName ) {
    if( m_keyboard ) {
        // Check for duplicate items
        m_itKeyListener = m_keyListeners.find( instanceName );
        if( m_itKeyListener == m_keyListeners.end() ) {
            m_keyListeners[ instanceName ] = keyListener;
        }
        else {
            // Duplicate Item
        }
    }
}
 
void InputManager::addMouseListener( OIS::MouseListener *mouseListener, const std::string& instanceName ) {
    if( m_mouse ) {
        // Check for duplicate items
        m_itMouseListener = m_mouseListeners.find( instanceName );
        if( m_itMouseListener == m_mouseListeners.end() ) {
            m_mouseListeners[ instanceName ] = mouseListener;
        }
        else {
            // Duplicate Item
        }
    }
}
 
void InputManager::addJoystickListener( OIS::JoyStickListener *joystickListener, const std::string& instanceName ) {
    if( m_joysticks.size() > 0 ) {
        // Check for duplicate items
        m_itJoystickListener = m_joystickListeners.find( instanceName );
        if( m_itJoystickListener == m_joystickListeners.end() ) {
            m_joystickListeners[ instanceName ] = joystickListener;
        }
        else {
            // Duplicate Item
        }
    }
}
 
void InputManager::removeKeyListener( const std::string& instanceName ) {
    // Check if item exists
    m_itKeyListener = m_keyListeners.find( instanceName );
    if( m_itKeyListener != m_keyListeners.end() ) {
        m_keyListeners.erase( m_itKeyListener );
    }
    else {
        // Doesn't Exist
    }
}
 
void InputManager::removeMouseListener( const std::string& instanceName ) {
    // Check if item exists
    m_itMouseListener = m_mouseListeners.find( instanceName );
    if( m_itMouseListener != m_mouseListeners.end() ) {
        m_mouseListeners.erase( m_itMouseListener );
    }
    else {
        // Doesn't Exist
    }
}
 
void InputManager::removeJoystickListener( const std::string& instanceName ) {
    // Check if item exists
    m_itJoystickListener = m_joystickListeners.find( instanceName );
    if( m_itJoystickListener != m_joystickListeners.end() ) {
        m_joystickListeners.erase( m_itJoystickListener );
    }
    else {
        // Doesn't Exist
    }
}
 
void InputManager::removeKeyListener( OIS::KeyListener *keyListener ) {
    m_itKeyListener    = m_keyListeners.begin();
    m_itKeyListenerEnd = m_keyListeners.end();
    for(; m_itKeyListener != m_itKeyListenerEnd; ++m_itKeyListener ) {
        if( m_itKeyListener->second == keyListener ) {
            m_keyListeners.erase( m_itKeyListener );
            break;
        }
    }
}
 
void InputManager::removeMouseListener( OIS::MouseListener *mouseListener ) {
    m_itMouseListener    = m_mouseListeners.begin();
    m_itMouseListenerEnd = m_mouseListeners.end();
    for(; m_itMouseListener != m_itMouseListenerEnd; ++m_itMouseListener ) {
        if( m_itMouseListener->second == mouseListener ) {
            m_mouseListeners.erase( m_itMouseListener );
            break;
        }
    }
}
 
void InputManager::removeJoystickListener( OIS::JoyStickListener *joystickListener ) {
    m_itJoystickListener    = m_joystickListeners.begin();
    m_itJoystickListenerEnd = m_joystickListeners.end();
    for(; m_itJoystickListener != m_itJoystickListenerEnd; ++m_itJoystickListener ) {
        if( m_itJoystickListener->second == joystickListener ) {
            m_joystickListeners.erase( m_itJoystickListener );
            break;
        }
    }
}
 
void InputManager::removeAllListeners( void ) {
    m_keyListeners.clear();
    m_mouseListeners.clear();
    m_joystickListeners.clear();
}
 
void InputManager::removeAllKeyListeners( void ) {
    m_keyListeners.clear();
}
 
void InputManager::removeAllMouseListeners( void ) {
    m_mouseListeners.clear();
}
 
void InputManager::removeAllJoystickListeners( void ) {
    m_joystickListeners.clear();
}
 
void InputManager::setWindowExtents( int width, int height ) {
    // Set mouse region (if window resizes, we should alter this to reflect as well)
    const OIS::MouseState &mouseState = m_mouse->getMouseState();
    mouseState.width  = width;
    mouseState.height = height;
}
 
OIS::Mouse* InputManager::getMouse( void ) {
    return m_mouse;
}
 
OIS::Keyboard* InputManager::getKeyboard( void ) {
    return m_keyboard;
}
 
OIS::JoyStick* InputManager::getJoystick( unsigned int index ) {
    // Make sure it's a valid index
    if( index < m_joysticks.size() ) {
        return m_joysticks[ index ];
    }
 
    return 0;
}
 
int InputManager::getNumOfJoysticks( void ) {
    // Cast to keep compiler happy ^^
    return (int) m_joysticks.size();
}
 
bool InputManager::keyPressed( const OIS::KeyEvent &e ) {
    m_itKeyListener    = m_keyListeners.begin();
    m_itKeyListenerEnd = m_keyListeners.end();
    for(; m_itKeyListener != m_itKeyListenerEnd; ++m_itKeyListener ) {
        if(!m_itKeyListener->second->keyPressed( e ))
            break;
    }
 
    return true;
}
 
bool InputManager::keyReleased( const OIS::KeyEvent &e ) {
    m_itKeyListener    = m_keyListeners.begin();
    m_itKeyListenerEnd = m_keyListeners.end();
    for(; m_itKeyListener != m_itKeyListenerEnd; ++m_itKeyListener ) {
        if(!m_itKeyListener->second->keyReleased( e ))
            break;
    }
 
    return true;
}
 
bool InputManager::mouseMoved( const OIS::MouseEvent &e ) {
    m_itMouseListener    = m_mouseListeners.begin();
    m_itMouseListenerEnd = m_mouseListeners.end();
    for(; m_itMouseListener != m_itMouseListenerEnd; ++m_itMouseListener ) {
        if(!m_itMouseListener->second->mouseMoved( e ))
            break;
    }
 
    return true;
}
 
bool InputManager::mousePressed( const OIS::MouseEvent &e, OIS::MouseButtonID id ) {
    m_itMouseListener    = m_mouseListeners.begin();
    m_itMouseListenerEnd = m_mouseListeners.end();
    for(; m_itMouseListener != m_itMouseListenerEnd; ++m_itMouseListener ) {
        if(!m_itMouseListener->second->mousePressed( e, id ))
            break;
    }
 
    return true;
}
 
bool InputManager::mouseReleased( const OIS::MouseEvent &e, OIS::MouseButtonID id ) {
    m_itMouseListener    = m_mouseListeners.begin();
    m_itMouseListenerEnd = m_mouseListeners.end();
    for(; m_itMouseListener != m_itMouseListenerEnd; ++m_itMouseListener ) {
        if(!m_itMouseListener->second->mouseReleased( e, id ))
            break;
    }
 
    return true;
}
 
bool InputManager::povMoved( const OIS::JoyStickEvent &e, int pov ) {
    m_itJoystickListener    = m_joystickListeners.begin();
    m_itJoystickListenerEnd = m_joystickListeners.end();
    for(; m_itJoystickListener != m_itJoystickListenerEnd; ++m_itJoystickListener ) {
        if(!m_itJoystickListener->second->povMoved( e, pov ))
            break;
    }
 
    return true;
}
 
bool InputManager::axisMoved( const OIS::JoyStickEvent &e, int axis ) {
    m_itJoystickListener    = m_joystickListeners.begin();
    m_itJoystickListenerEnd = m_joystickListeners.end();
    for(; m_itJoystickListener != m_itJoystickListenerEnd; ++m_itJoystickListener ) {
        if(!m_itJoystickListener->second->axisMoved( e, axis ))
            break;
    }
 
    return true;
}
 
bool InputManager::sliderMoved( const OIS::JoyStickEvent &e, int sliderID ) {
    m_itJoystickListener    = m_joystickListeners.begin();
    m_itJoystickListenerEnd = m_joystickListeners.end();
    for(; m_itJoystickListener != m_itJoystickListenerEnd; ++m_itJoystickListener ) {
        if(!m_itJoystickListener->second->sliderMoved( e, sliderID ))
            break;
    }
 
    return true;
}
 
bool InputManager::buttonPressed( const OIS::JoyStickEvent &e, int button ) {
    m_itJoystickListener    = m_joystickListeners.begin();
    m_itJoystickListenerEnd = m_joystickListeners.end();
    for(; m_itJoystickListener != m_itJoystickListenerEnd; ++m_itJoystickListener ) {
        if(!m_itJoystickListener->second->buttonPressed( e, button ))
            break;
    }
 
    return true;
}
 
bool InputManager::buttonReleased( const OIS::JoyStickEvent &e, int button ) {
    m_itJoystickListener    = m_joystickListeners.begin();
    m_itJoystickListenerEnd = m_joystickListeners.end();
    for(; m_itJoystickListener != m_itJoystickListenerEnd; ++m_itJoystickListener ) {
        if(!m_itJoystickListener->second->buttonReleased( e, button ))
            break;
    }
 
    return true;
}
 
InputManager* InputManager::getSingletonPtr( void ) {
    if( !m_inputManager ) {
        m_inputManager = new InputManager();
    }
 
    return m_inputManager;
}